package com.fengwk.support.doc;

import java.io.File;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;

import org.docx4j.TextUtils;
import org.docx4j.XmlUtils;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.jaxb.Context;
import org.docx4j.jaxb.XPathBinderAssociationIsPartialException;
import org.docx4j.model.properties.table.tr.TrHeight;
import org.docx4j.model.structure.PageSizePaper;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.BooleanDefaultTrue;
import org.docx4j.wml.Br;
import org.docx4j.wml.CTBackground;
import org.docx4j.wml.CTBorder;
import org.docx4j.wml.CTEm;
import org.docx4j.wml.CTHeight;
import org.docx4j.wml.CTLineNumber;
import org.docx4j.wml.CTShd;
import org.docx4j.wml.CTSignedHpsMeasure;
import org.docx4j.wml.CTSignedTwipsMeasure;
import org.docx4j.wml.CTTblCellMar;
import org.docx4j.wml.CTTextScale;
import org.docx4j.wml.CTVerticalAlignRun;
import org.docx4j.wml.CTVerticalJc;
import org.docx4j.wml.Color;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Document;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.Highlight;
import org.docx4j.wml.HpsMeasure;
import org.docx4j.wml.Jc;
import org.docx4j.wml.JcEnumeration;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.PPr;
import org.docx4j.wml.ParaRPr;
import org.docx4j.wml.R;
import org.docx4j.wml.RFonts;
import org.docx4j.wml.RPr;
import org.docx4j.wml.STBorder;
import org.docx4j.wml.STBrType;
import org.docx4j.wml.STEm;
import org.docx4j.wml.STLineNumberRestart;
import org.docx4j.wml.STLineSpacingRule;
import org.docx4j.wml.STPageOrientation;
import org.docx4j.wml.STShd;
import org.docx4j.wml.STVerticalAlignRun;
import org.docx4j.wml.STVerticalJc;
import org.docx4j.wml.SectPr;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.TblBorders;
import org.docx4j.wml.TblGrid;
import org.docx4j.wml.TblGridCol;
import org.docx4j.wml.TblPr;
import org.docx4j.wml.TblWidth;
import org.docx4j.wml.Tc;
import org.docx4j.wml.TcPr;
import org.docx4j.wml.Text;
import org.docx4j.wml.TextDirection;
import org.docx4j.wml.Tr;
import org.docx4j.wml.TrPr;
import org.docx4j.wml.U;
import org.docx4j.wml.UnderlineEnumeration;
import org.docx4j.wml.P.Hyperlink;
import org.docx4j.wml.PPrBase.Ind;
import org.docx4j.wml.PPrBase.PBdr;
import org.docx4j.wml.PPrBase.Spacing;
import org.docx4j.wml.SectPr.PgBorders;
import org.docx4j.wml.SectPr.PgMar;
import org.docx4j.wml.SectPr.PgSz;
import org.docx4j.wml.TcPrInner.GridSpan;
import org.docx4j.wml.TcPrInner.HMerge;
import org.docx4j.wml.TcPrInner.VMerge;

import com.fengwk.support.util.ArrayUtils;
import com.fengwk.support.util.CollectionUtils;
import com.fengwk.support.util.IoUtils;
import com.fengwk.support.util.LoggerUtils;
import com.fengwk.support.util.StringUtils;
import com.fengwk.support.util.LoggerUtils.Logger;

/**
 * 用于书写.docx文档
 * 
 * @author fengwk
 *
 */
public class Docx implements Serializable {

    private static final long serialVersionUID = 6889254106444235742L;

    private static final ObjectFactory factory = Context.getWmlObjectFactory();

    public static final String DEFAULT_FONT_FAMILY_EN = "Times New Roman";

    public static final String DEFAULT_FONT_FAMILY_CN = "宋体";

    public static final long DEFAULT_FONT_SIZE = 22L;

    private static Logger LOG = LoggerUtils.getLogger(Docx.class);

    /**
     * 文档
     */
    WordprocessingMLPackage wmlp;

    /**
     * 当前段落
     */
    private P currentP;

    /**
     * 当前运行块
     */
    private R currentR;

    /**
     * 文档样式
     */
    DocStyle docStyle;

    /**
     * 段落样式
     */
    PStyle pStyle;

    /**
     * 运行块样式
     */
    RStyle rStyle;

    // private Tbl currentTbl;
    private int uniqueId;

    /* 构造 */

    /**
     * 构造一个默认参数的Docx实例
     */
    public Docx() {
        this(PageSizePaper.A4, false);
    }

    /**
     * 构造一个Docx实例
     * 
     * @param sz
     * @param landscape
     */
    public Docx(PageSizePaper sz, boolean landscape) {
        try {
            wmlp = WordprocessingMLPackage.createPackage(sz, landscape);
            init();
        } catch (InvalidFormatException e) {
            LOG.error(e);
        }
    }

    /**
     * 使用已有的docx文件构建一个Docx实例 这个方法在jdk8下有问题,需要额外使用xerces包解决
     * https://segmentfault.com/a/1190000010162316
     * 
     * @param file
     */
    public Docx(File file) {
        try {
            wmlp = WordprocessingMLPackage.load(file);
            init();
        } catch (Docx4JException e) {
            LOG.error(e);
        }
    }

    /**
     * 使用已有的docx文件构建一个Docx实例 这个方法在jdk8下有问题,需要额外使用xerces包解决
     * https://segmentfault.com/a/1190000010162316
     * 
     * @param file
     * @param password
     */
    public Docx(File file, String password) {
        try {
            wmlp = (WordprocessingMLPackage) WordprocessingMLPackage.load(file, password);
            init();
        } catch (Docx4JException e) {
            LOG.error(e);
        }
    }

    // 初始化
    private void init() {
        newDocStyle();
        newPStyle();
        newRStyle();
        newP();
    }

    /* 文档操作相关 */

    /**
     * 保存为docx字节流
     * 
     * @return
     */
    public byte[] save() {
        return IoUtils.toBytes(os -> save(os));
    }

    /**
     * 保存到指定路径位置
     * 
     * @param path
     */
    public void save(String path) {
        save(IoUtils.buildOutputStream(path));
    }

    /**
     * 保存到指定文件位置
     * 
     * @param file
     * @return
     */
    public void save(File file) {
        save(IoUtils.buildOutputStream(file));
    }

    /**
     * 保存到指定输出流
     * 
     * @param os
     */
    public void save(OutputStream os) {
        try {
            wmlp.save(os);
        } catch (Docx4JException e) {
            LOG.error(e);
        }
    }
    
    /**
     * 新建一个与当前docx关联的文档样式
     * 
     * @return
     */
    public DocStyle newDocStyle() {
        docStyle = new DocStyle();
        return docStyle;
    }
    
    /**
     * 新建一个与当前docx关联的段落样式
     * 
     * @return
     */
    public PStyle newPStyle() {
        pStyle = new PStyle();
        return pStyle;
    }

    /**
     * 新建一个与当前docx关联的运行块样式
     * 
     * @return
     */
    public RStyle newRStyle() {
        rStyle = new RStyle();
        return rStyle;
    }

    /**
     * 新起一个段落
     * 
     * @return
     */
    public Docx newP() {
        currentP = factory.createP();
        try {
            wmlp.getMainDocumentPart().getContents().getBody().getContent().add(currentP);
            pStyle.applyAll();
        } catch (Docx4JException e) {
            LOG.error(e);
        }
        newR();
        return this;
    }

    /**
     * 新起一个运行块
     * 
     * @return
     */
    public Docx newR() {
        if (currentP == null) {
            newP();
        } else {
            currentR = factory.createR();
            currentP.getContent().add(currentR);
            rStyle.applyAll();
        }
        return this;
    }
    
    /**
     * 创建一个新的表格对象
     * 
     * @return
     */
    public Table newTable(int rows, int cols) {
        return new Table(rows, cols);
    }

    /**
     * 添加文本
     * 
     * @param value
     * @return
     */
    public Docx append(String text) {
        String[] lines = StringUtils.splitLine(text);
        if (ArrayUtils.isNotBlank(lines)) {
            for (int i = 0; i < lines.length; i++) {
                String line = lines[i];
                currentR.getContent().add(createText(line));
                if (i != lines.length - 1)
                    br();
            }
        }
        return this;
    }

    /**
     * 添加图片
     * 
     * @param img
     * @return
     * @throws Exception 
     */
    public Docx append(byte[] img) throws Exception {
        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wmlp, img);
        Inline inline = imagePart.createImageInline(null, null, uniqueId++, uniqueId++, false);
        Drawing drawing = factory.createDrawing();
        drawing.getAnchorOrInline().add(inline);
        currentR.getContent().add(drawing);
        return this;
    }

    /**
     * 添加超链接
     * 
     * @param text
     * @param url
     * @return
     */
    public Docx append(String text, String url) {
        RelationshipsPart relPart = wmlp.getMainDocumentPart().getRelationshipsPart();
        Relationship rel = new Relationship();
        rel.setType(Namespaces.HYPERLINK);
        rel.setTarget(url);
        rel.setTargetMode("External");
        relPart.addRelationship(rel);
        StringBuilder sb = new StringBuilder();
        String fontFamilyCn = DEFAULT_FONT_FAMILY_CN;
        String fontFamilyEn = DEFAULT_FONT_FAMILY_EN;
        long fontSize = DEFAULT_FONT_SIZE;
        if (rStyle.fontFamilyCn != null)
            fontFamilyCn = rStyle.fontFamilyCn;
        if (rStyle.fontFamilyEn != null)
            fontFamilyEn = rStyle.fontFamilyEn;
        if (rStyle.fontSize != null)
            fontSize = rStyle.fontSize;
        sb.append("<w:hyperlink r:id=\"");
        sb.append(rel.getId());
        sb.append("\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" ");
        sb.append("xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" >");
        sb.append("<w:r><w:rPr><w:rStyle w:val=\"Hyperlink\" />");
        sb.append("<w:rFonts  w:ascii=\"");
        sb.append(fontFamilyEn);
        sb.append("\"  w:hAnsi=\"");
        sb.append(fontFamilyEn);
        sb.append("\"  w:eastAsia=\"");
        sb.append(fontFamilyCn);
        sb.append("\" w:hint=\"eastAsia\"/>");
        sb.append("<w:sz w:val=\"");
        sb.append(fontSize);
        sb.append("\"/><w:szCs w:val=\"");
        sb.append(fontSize);
        sb.append("\"/></w:rPr><w:t>");
        sb.append(text);
        sb.append("</w:t></w:r></w:hyperlink>");
        try {
            Hyperlink hl = (Hyperlink) XmlUtils.unmarshalString(sb.toString());
            currentR.getContent().add(hl);
        } catch (JAXBException e) {
            LOG.error(e);
        }
        return this;
    }
    
    /**
     * 挂载表格对象
     * 
     * @param table
     * @return
     */
    public Docx append(Table table) {
        wmlp.getMainDocumentPart().addObject(table.tbl);
        return this;
    }

    /**
     * 换行
     * 
     * @return
     */
    public Docx br() {
        br(STBrType.TEXT_WRAPPING);
        return this;
    }

    /**
     * 下一页
     * 
     * @return
     */
    public Docx nextPage() {
        br(STBrType.PAGE);
        return this;
    }

    /* 文档样式相关 */

    /**
     * 文档样式
     * 
     * @return
     */
    public DocStyle docStyle() {
        return docStyle;
    }

    /**
     * 段落样式
     * 
     * @return
     */
    public PStyle pStyle() {
        return pStyle;
    }

    /**
     * 运行块样式
     * 
     * @return
     */
    public RStyle rStyle() {
        return rStyle;
    }

    /**
     * 获取文档的可用宽度
     * 
     * @param wmlp
     * @return
     */
    public int docWidth() {
        return getSection().getPageDimensions().getWritableWidthTwips();
    }
    
    /**
     * 获取当前文档中的所有表格
     * 
     * @return
     */
    public List<Table> getAllTables() {
        MainDocumentPart mainDocPart = wmlp.getMainDocumentPart();
        List<Object> objList = getAllElementFromObject(mainDocPart, Tbl.class);
        if (objList == null) {
            return null;
        }
        List<Table> tbList = new ArrayList<Table>();
        for (Object obj : objList) {
            if (obj instanceof Tbl) {
                Tbl tbl = (Tbl) obj;
                tbList.add(new Table(tbl));
            }
        }
        return tbList;
    }

    // 段落添加分割或页面Break(分页符)
    private Docx br(STBrType sTBrType) {
        Br br = factory.createBr();
        if (sTBrType != null)
            br.setType(sTBrType);
        currentR.getContent().add(br);
        return this;
    }

    private SectionWrapper getSection() {
        List<SectionWrapper> sections = wmlp.getDocumentModel().getSections();
        return sections.get(sections.size() - 1);
    }

    private SectPr getSectPr() {
        return getSection().getSectPr();
    }

    // 生成文本
    private Text createText(String value) {
        Text text = factory.createText();
        text.setSpace("preserve");
        text.setValue(value);
        return text;
    }
    
    private static BooleanDefaultTrue createBooleanDefaultTrue(boolean b) {
        BooleanDefaultTrue bdt = factory.createBooleanDefaultTrue();
        bdt.setVal(b);
        return bdt;
    }
    
    private static String getElementContent(Object obj) {
        StringWriter stringWriter = new StringWriter();
        try {
            TextUtils.extractText(obj, stringWriter);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return stringWriter.toString();
    }
    
    // 得到指定类型的元素
    private List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
        List<Object> result = new ArrayList<Object>();
        if (obj instanceof JAXBElement)
            obj = ((JAXBElement<?>) obj).getValue();
        if (obj != null && obj.getClass().equals(toSearch))
            result.add(obj);
        else if (obj instanceof ContentAccessor) {
            List<?> children = ((ContentAccessor) obj).getContent();
            for (Object child : children) {
                result.addAll(getAllElementFromObject(child, toSearch));
            }
        }
        return result;
    }
    
    // 获取NodeList
    public List<Object> getObjectByXpath(String xpath) throws XPathBinderAssociationIsPartialException, JAXBException  {
        final List<Object> jaxbNodes = wmlp.getMainDocumentPart().getJAXBNodesViaXPath(xpath, true);
        return jaxbNodes;
    }

    /**
     * 文档样式
     * 
     * @author fengwk
     *
     */
    public class DocStyle implements Serializable, Cloneable {

        private static final long serialVersionUID = 1783945503742597970L;

        private DocStyle() {
        }

        /**
         * 回到Docx中,为了更好支持链式调用
         * 
         * @return
         */
        public Docx docx() {
            return Docx.this;
        }

        /**
         * 段落样式
         * 
         * @return
         */
        public PStyle pStyle() {
            return pStyle;
        }

        /**
         * 运行块样式
         * 
         * @return
         */
        public RStyle rStyle() {
            return rStyle;
        }

        /**
         * 设置文档背景颜色
         * 
         * @param backgroundColor 16进制
         */
        public DocStyle backgroundColor(String backgroundColor) {
            try {
                Document doc = wmlp.getMainDocumentPart().getContents();
                CTBackground bkground = doc.getBackground();
                if (bkground == null) {
                    bkground = factory.createCTBackground();
                    bkground.setColor(backgroundColor);
                }
                doc.setBackground(bkground);
            } catch (Docx4JException e) {
                LOG.error(e);
            }
            return this;
        }

        /**
         * 设置页面宽度
         * 
         * @param width
         * @return
         */
        public DocStyle width(long width) {
            getPgSz().setW(BigInteger.valueOf(width));
            return this;
        }

        /**
         * 设置页面高度
         * 
         * @param height
         * @return
         */
        public DocStyle height(long height) {
            getPgSz().setH(BigInteger.valueOf(height));
            return this;
        }

        /**
         * 文档方向<br/>
         * {@link STPageOrientation#PORTRAIT} 竖向<br/>
         * {@link STPageOrientation#LANDSCAPE} 横向
         * 
         * @param orientation
         * @return
         */
        public DocStyle orientation(STPageOrientation orientation) {
            getPgSz().setOrient(orientation);
            return this;
        }

        private PgSz getPgSz() {
            return getSectPr().getPgSz();
        }

        /**
         * 设置页面上边框
         * 
         * @param borderTop
         * @return
         */
        public DocStyle borderTop(CTBorder borderTop) {
            getPgBorders().setTop(borderTop);
            return this;
        }

        /**
         * 设置页面右边框
         * 
         * @param borderRight
         * @return
         */
        public DocStyle borderRight(CTBorder borderRight) {
            getPgBorders().setRight(borderRight);
            return this;
        }

        /**
         * 设置页面下边框
         * 
         * @param borderBottom
         * @return
         */
        public DocStyle borderBottom(CTBorder borderBottom) {
            getPgBorders().setBottom(borderBottom);
            return this;
        }

        /**
         * 设置页面左边框
         * 
         * @param borderLeft
         * @return
         */
        public DocStyle borderLeft(CTBorder borderLeft) {
            getPgBorders().setLeft(borderLeft);
            return this;
        }

        private PgBorders getPgBorders() {
            SectPr sectPr = getSectPr();
            PgBorders pb = sectPr.getPgBorders();
            if (pb == null) {
                pb = factory.createSectPrPgBorders();
                sectPr.setPgBorders(pb);
            }
            return pb;
        }

        /**
         * 设置页面上边距
         * 
         * @param marginTop
         * @return
         */
        public DocStyle marginTop(long marginTop) {
            getPgMar().setTop(BigInteger.valueOf(marginTop));
            return this;
        }

        /**
         * 设置页面右边距
         * 
         * @param marginTop
         * @return
         */
        public DocStyle marginRight(long marginRight) {
            getPgMar().setRight(BigInteger.valueOf(marginRight));
            return this;
        }

        /**
         * 设置页面下边距
         * 
         * @param marginTop
         * @return
         */
        public DocStyle marginBottom(long marginBottom) {
            getPgMar().setBottom(BigInteger.valueOf(marginBottom));
            return this;
        }

        /**
         * 设置页面左边距
         * 
         * @param marginTop
         * @return
         */
        public DocStyle marginLeft(long marginLeft) {
            getPgMar().setLeft(BigInteger.valueOf(marginLeft));
            return this;
        }

        private PgMar getPgMar() {
            SectPr sectPr = getSectPr();
            PgMar pg = sectPr.getPgMar();
            if (pg == null) {
                pg = factory.createSectPrPgMar();
                sectPr.setPgMar(pg);
            }
            return pg;
        }

        /**
         * 设置起始行号
         * 
         * @param wmlp
         * @param lineNumStart
         * @return
         */
        public DocStyle lineNumStart(long lineNumStart) {
            getCTLineNumber().setStart(BigInteger.valueOf(lineNumStart));
            return this;
        }

        /**
         * 设置行号间隔
         * 
         * @param lineNumCountBy
         * @return
         */
        public DocStyle lineNumCountBy(long lineNumCountBy) {
            getCTLineNumber().setCountBy(BigInteger.valueOf(lineNumCountBy));
            return this;
        }

        /**
         * 设置行号距正文距离,1厘米=567
         * 
         * @param lineNumDistance
         * @return
         */
        public DocStyle lineNumDistance(long lineNumDistance) {
            getCTLineNumber().setDistance(BigInteger.valueOf(lineNumDistance));
            return this;
        }

        /**
         * 设置行号类型</br>
         * {@link STLineNumberRestart#CONTINUOUS} 连续编号
         * {@link STLineNumberRestart#NEW_PAGE} 每页重新编号
         * {@link STLineNumberRestart#NEW_SECTION} 每节重新编号
         * 
         * @param lineNumRestart
         * @return
         */
        public DocStyle lineNumRestart(STLineNumberRestart lineNumRestart) {
            getCTLineNumber().setRestart(lineNumRestart);
            return this;
        }

        private CTLineNumber getCTLineNumber() {
            SectPr sectPr = getSectPr();
            CTLineNumber LineNum = sectPr.getLnNumType();
            if (LineNum == null) {
                LineNum = factory.createCTLineNumber();
                sectPr.setLnNumType(LineNum);
            }
            return LineNum;
        }

        /**
         * 设置文字方向
         * 
         * @param docTextDirection
         * @return
         */
        public DocStyle textDirection(String textDirection) {
            SectPr sectPr = getSectPr();
            TextDirection textDir = sectPr.getTextDirection();
            if (textDir == null) {
                textDir = factory.createTextDirection();
                sectPr.setTextDirection(textDir);
            }
            textDir.setVal(textDirection);
            return this;
        }

        /**
         * 设置垂直对齐方式
         * 
         * @param valign
         * @return
         */
        public DocStyle valign(STVerticalJc valign) {
            SectPr sectPr = getSectPr();
            CTVerticalJc valign0 = sectPr.getVAlign();
            if (valign0 == null) {
                valign0 = factory.createCTVerticalJc();
                sectPr.setVAlign(valign0);
            }
            valign0.setVal(valign);
            return this;
        }

    }

    /**
     * 段落样式
     * 
     * @author fengwk
     *
     */
    public class PStyle implements Serializable {

        private static final long serialVersionUID = 9031500983459320397L;

        private JcEnumeration align;
        private Boolean suppressLineNum;
        private STShd shd;
        private String shdColor;
        private Long spacingBefore;
        private Long spacingAfter;
        private Long spacingBeforeLines;
        private Long spacingAfterLines;
        private Long spacingLine;
        private STLineSpacingRule spacingLineRule;
        private Long indFirstLine;
        private Long indFirstLineChars;
        private Long indHanging;
        private Long indHangingChars;
        private Long indLeft;
        private Long indLeftChars;
        private Long indRight;
        private Long indRightChars;
        private CTBorder borderTop;
        private CTBorder borderRight;
        private CTBorder borderBottom;
        private CTBorder borderLeft;
        private Boolean vanish;
        
        private PStyle() {}

        /**
         * 使用所有已设置的属性渲染
         */
        private void applyAll() {
            if (align != null)
                align(align);
            if (suppressLineNum != null)
                suppressLineNum(suppressLineNum);
            if (shd != null)
                shd(shd);
            if (shdColor != null)
                shdColor(shdColor);
            if (spacingBefore != null)
                spacingBefore(spacingBefore);
            if (spacingAfter != null)
                spacingAfter(spacingAfter);
            if (spacingBeforeLines != null)
                spacingBeforeLines(spacingBeforeLines);
            if (spacingAfterLines != null)
                spacingAfterLines(spacingAfterLines);
            if (spacingLine != null)
                spacingLine(spacingLine);
            if (spacingLineRule != null)
                spacingLineRule(spacingLineRule);
            if (indFirstLine != null)
                indFirstLine(indFirstLine);
            if (indFirstLineChars != null)
                indFirstLineChars(indFirstLineChars);
            if (indHanging != null)
                indHanging(indHanging);
            if (indHangingChars != null)
                indHangingChars(indHangingChars);
            if (indLeft != null)
                indLeft(indLeft);
            if (indLeftChars != null)
                indLeftChars(indLeftChars);
            if (indRight != null)
                indRight(indRight);
            if (indRightChars != null)
                indRightChars(indRightChars);
            if (borderTop != null)
                borderTop(borderTop);
            if (borderRight != null)
                borderRight(borderRight);
            if (borderBottom != null)
                borderBottom(borderBottom);
            if (borderLeft != null)
                borderLeft(borderLeft);
            if (vanish != null)
                vanish(vanish);
        }
        
        /**
         * 回到Docx中,为了更好支持链式调用
         * 
         * @return
         */
        public Docx docx() {
            return Docx.this;
        }

        /**
         * 文档样式
         * 
         * @return
         */
        public DocStyle docStyle() {
            return docStyle;
        }

        /**
         * 运行块样式
         * 
         * @return
         */
        public RStyle rStyle() {
            return rStyle;
        }

        /**
         * 设置段落水平对齐方式
         * 
         * @param p
         * @param jcAlign
         * @return
         */
        public PStyle align(JcEnumeration align) {
            this.align = align;
            PPr ppr = getPPr();
            Jc align0 = factory.createJc();
            align0.setVal(align);
            ppr.setJc(align0);
            return this;
        }

        /**
         * 设置段落禁止行号,禁止用于当前行号
         * 
         * @param p
         * @param suppressLineNum
         */
        public PStyle suppressLineNum(boolean suppressLineNum) {
            this.suppressLineNum = suppressLineNum;
            getPPr().setSuppressLineNumbers(createBooleanDefaultTrue(suppressLineNum));
            return this;
        }

        /**
         * 设置段落底纹
         * 
         * @param shdType
         * @return
         */
        public PStyle shd(STShd shd) {
            this.shd = shd;
            getCTShd().setVal(shd);
            return this;
        }

        /**
         * 设置段落底纹颜色
         * 
         * @param shdColor 16进制
         * @return
         */
        public PStyle shdColor(String shdColor) {
            this.shdColor = shdColor;
            getCTShd().setColor(shdColor);
            return this;
        }

        private CTShd getCTShd() {
            PPr ppr = getPPr();
            CTShd ctShd = ppr.getShd();
            if (ctShd == null) {
                ctShd = factory.createCTShd();
                ppr.setShd(ctShd);
            }
            return ctShd;
        }

        /**
         * 段前磅数
         * 
         * @param spacingBefore
         * @return
         */
        public PStyle spacingBefore(long spacingBefore) {
            this.spacingBefore = spacingBefore;
            getPSpacing().setBefore(BigInteger.valueOf(spacingBefore));
            return this;
        }

        /**
         * 段后磅数
         * 
         * @param spacingAfter
         * @return
         */
        public PStyle spacingAfter(long spacingAfter) {
            this.spacingAfter = spacingAfter;
            getPSpacing().setAfter(BigInteger.valueOf(spacingAfter));
            return this;
        }

        /**
         * 段前行数
         * 
         * @param spacingBeforeLines
         * @return
         */
        public PStyle spacingBeforeLines(long spacingBeforeLines) {
            this.spacingBeforeLines = spacingBeforeLines;
            getPSpacing().setBeforeLines(BigInteger.valueOf(spacingBeforeLines));
            return this;
        }

        /**
         * 段后行数
         * 
         * @param spacingAfterLines
         * @return
         */
        public PStyle spacingAfterLines(long spacingAfterLines) {
            this.spacingAfterLines = spacingAfterLines;
            getPSpacing().setAfterLines(BigInteger.valueOf(spacingAfterLines));
            return this;
        }

        /**
         * 行距值,1磅=20 1行=100 单倍行距=240
         * 
         * @param spacingLine
         * @return
         */
        public PStyle spacingLine(long spacingLine) {
            this.spacingLine = spacingLine;
            getPSpacing().setLine(BigInteger.valueOf(spacingLine));
            return this;
        }

        /**
         * 行距规则</br>
         * {@link STLineSpacingRule#AUTO} 自动</br>
         * {@link STLineSpacingRule#EXACT} 固定</br>
         * {@link STLineSpacingRule#AT_LEAST} 最小
         * 
         * @param spacingLineRule
         * @return
         */
        public PStyle spacingLineRule(STLineSpacingRule spacingLineRule) {
            this.spacingLineRule = spacingLineRule;
            getPSpacing().setLineRule(spacingLineRule);
            return this;
        }

        private Spacing getPSpacing() {
            PPr ppr = getPPr();
            Spacing spacing = ppr.getSpacing();
            if (spacing == null) {
                spacing = factory.createPPrBaseSpacing();
                ppr.setSpacing(spacing);
            }
            return spacing;
        }

        /**
         * 首行缩进 1厘米≈567
         * 
         * @param indFirstLine
         * @return
         */
        public PStyle indFirstLine(long indFirstLine) {
            this.indFirstLine = indFirstLine;
            getInd().setFirstLine(BigInteger.valueOf(indFirstLine));
            return this;
        }

        public PStyle indFirstLineChars(long indFirstLineChars) {
            this.indFirstLineChars = indFirstLineChars;
            getInd().setFirstLineChars(BigInteger.valueOf(indFirstLineChars));
            return this;
        }

        public PStyle indHanging(long indHanging) {
            this.indHanging = indHanging;
            getInd().setHanging(BigInteger.valueOf(indHanging));
            return this;
        }

        public PStyle indHangingChars(long indHangingChars) {
            this.indHangingChars = indHangingChars;
            getInd().setHangingChars(BigInteger.valueOf(indHangingChars));
            return this;
        }

        public PStyle indLeft(long indLeft) {
            this.indLeft = indLeft;
            getInd().setLeft(BigInteger.valueOf(indLeft));
            return this;
        }

        public PStyle indLeftChars(long indLeftChars) {
            this.indLeftChars = indLeftChars;
            getInd().setLeftChars(BigInteger.valueOf(indLeftChars));
            return this;
        }

        public PStyle indRight(long indRight) {
            this.indRight = indRight;
            getInd().setRight(BigInteger.valueOf(indRight));
            return this;
        }

        public PStyle indRightChars(long indRightChars) {
            this.indRightChars = indRightChars;
            getInd().setRightChars(BigInteger.valueOf(indRightChars));
            return this;
        }

        private Ind getInd() {
            PPr ppr = getPPr();
            Ind ind = ppr.getInd();
            if (ind == null) {
                ind = new Ind();
                ppr.setInd(ind);
            }
            return ind;
        }

        /**
         * 段落上边框
         * 
         * @param borderTop
         * @return
         */
        public PStyle borderTop(CTBorder borderTop) {
            this.borderTop = borderTop;
            getPBdr().setTop(borderTop);
            return this;
        }

        /**
         * 段落右边框
         * 
         * @param borderRight
         * @return
         */
        public PStyle borderRight(CTBorder borderRight) {
            this.borderRight = borderRight;
            getPBdr().setRight(borderRight);
            return this;
        }

        /**
         * 段落下边框
         * 
         * @param borderBottom
         * @return
         */
        public PStyle borderBottom(CTBorder borderBottom) {
            this.borderBottom = borderBottom;
            getPBdr().setBottom(borderBottom);
            return this;
        }

        /**
         * 段落左边框
         * 
         * @param borderLeft
         * @return
         */
        public PStyle borderLeft(CTBorder borderLeft) {
            this.borderLeft = borderLeft;
            getPBdr().setLeft(borderLeft);
            return this;
        }

        private PBdr getPBdr() {
            PPr ppr = getPPr();
            PBdr pBdr = ppr.getPBdr();
            if (pBdr == null) {
                pBdr = factory.createPPrBasePBdr();
                ppr.setPBdr(pBdr);
            }
            return pBdr;
        }

        /**
         * 段落隐藏
         * 
         * @param vanish
         * @return
         */
        public PStyle vanish(boolean vanish) {
            this.vanish = vanish;
            PPr ppr = getPPr();
            ParaRPr parRpr = ppr.getRPr();
            if (parRpr == null) {
                parRpr = new ParaRPr();
                ppr.setRPr(parRpr);
            }
            parRpr.setVanish(createBooleanDefaultTrue(vanish));
            return this;
        }

        private PPr getPPr() {
            PPr ppr = currentP.getPPr();
            if (ppr == null) {
                ppr = factory.createPPr();
                currentP.setPPr(ppr);
            }
            return ppr;
        }

    }

    /**
     * 运行块样式
     * 
     * @author fengwk
     *
     */
    public class RStyle implements Serializable {

        private static final long serialVersionUID = -8750750495621919454L;

        private Long fontSize;
        private String fontFamilyCn;
        private String fontFamilyEn;
        private String fontColor;
        private Boolean bold;
        private Boolean italic;
        private UnderlineEnumeration underline;
        private String hightLight;
        private Boolean strike;
        private Boolean dStrike;
        private Long borderWidth;
        private STBorder border;
        private String borderColor;
        private Long borderSpace;
        private STEm em;
        private Boolean outline;
        private Boolean imprint;
        private Boolean emboss;
        private STVerticalAlignRun verticalAlignRun;
        private Integer scale;
        private Long spacing;
        private Long position;
        private Boolean vanish;
        private Boolean shadow;
        private STShd shd;
        
        private RStyle() {}

        /**
         * 使用所有已设置的属性渲染
         */
        private void applyAll() {
            if (fontSize != null)
                fontSize(fontSize);
            if (fontFamilyCn != null)
                fontFamilyCn(fontFamilyCn);
            if (fontFamilyEn != null)
                fontFamilyEn(fontFamilyEn);
            if (fontColor != null)
                fontColor(fontColor);
            if (bold != null)
                bold(bold);
            if (italic != null)
                italic(italic);
            if (underline != null)
                underline(underline);
            if (hightLight != null)
                hightLight(hightLight);
            if (strike != null)
                strike(strike);
            if (dStrike != null)
                dStrike(dStrike);
            if (borderWidth != null)
                borderWidth(borderWidth);
            if (border != null)
                border(border);
            if (borderColor != null)
                borderColor(borderColor);
            if (borderSpace != null)
                borderSpace(borderSpace);
            if (em != null)
                em(em);
            if (outline != null)
                outline(outline);
            if (imprint != null)
                imprint(imprint);
            if (emboss != null)
                emboss(emboss);
            if (verticalAlignRun != null)
                verticalAlignRun(verticalAlignRun);
            if (scale != null)
                scale(scale);
            if (spacing != null)
                spacing(spacing);
            if (position != null)
                position(position);
            if (vanish != null)
                vanish(vanish);
            if (shadow != null)
                shadow(shadow);
            if (shd != null)
                shd(shd);
        }
        
        /**
         * 回到Docx中,为了更好支持链式调用
         * 
         * @return
         */
        public Docx docx() {
            return Docx.this;
        }

        /**
         * 文档样式
         * 
         * @return
         */
        public DocStyle docStyle() {
            return docStyle;
        }

        /**
         * 段落样式
         * 
         * @return
         */
        public PStyle pStyle() {
            return pStyle;
        }

        /**
         * 设置字符大小
         * 
         * @param fontSize
         */
        public RStyle fontSize(long fontSize) {
            this.fontSize = fontSize;
            HpsMeasure hm = factory.createHpsMeasure();
            hm.setVal(BigInteger.valueOf(fontSize));
            RPr rpr = getRPr();
            rpr.setSz(hm);
            rpr.setSzCs(hm);
            return this;
        }

        /**
         * 设置中文字体
         * 
         * @param fontFamilyCn
         * @return
         */
        public RStyle fontFamilyCn(String fontFamilyCn) {
            this.fontFamilyCn = fontFamilyCn;
            fontFamily(fontFamilyCn, null);
            return this;
        }

        /**
         * 设置英文字体
         * 
         * @param fontFamilyCn
         * @return
         */
        public RStyle fontFamilyEn(String fontFamilyEn) {
            this.fontFamilyEn = fontFamilyEn;
            fontFamily(null, fontFamilyEn);
            return this;
        }

        // 设置字体
        private RStyle fontFamily(String fontFamilyCn, String fontFamilyEn) {
            RPr rpr = getRPr();
            RFonts rf = rpr.getRFonts();
            if (rf == null) {
                rf = factory.createRFonts();
                rpr.setRFonts(rf);
            }
            if (StringUtils.isNotBlank(fontFamilyEn)) {
                rf.setAscii(fontFamilyEn);
                rf.setHAnsi(fontFamilyEn);
            }
            if (StringUtils.isNotBlank(fontFamilyCn))
                rf.setEastAsia(fontFamilyCn);
            return this;
        }

        /**
         * 设置字符颜色
         * 
         * @param fontColor 16进制
         * @return
         */
        public RStyle fontColor(String fontColor) {
            this.fontColor = fontColor;
            if (fontColor != null) {
                Color c = factory.createColor();
                c.setVal(fontColor);
                getRPr().setColor(c);
            }
            return this;
        }

        /**
         * 设置粗体
         * 
         * @param bold
         * @return
         */
        public RStyle bold(boolean bold) {
            this.bold = bold;
            getRPr().setB(createBooleanDefaultTrue(bold));
            return this;
        }

        /**
         * 设置斜体
         * 
         * @param italic
         * @return
         */
        public RStyle italic(boolean italic) {
            this.italic = italic;
            getRPr().setI(createBooleanDefaultTrue(italic));
            return this;
        }

        /**
         * 设置下划线
         * 
         * @param underline
         * @return
         */
        public RStyle underline(UnderlineEnumeration underline) {
            this.underline = underline;
            U val = factory.createU();
            val.setVal(underline);
            getRPr().setU(val);
            return this;
        }

        /**
         * 设置高亮文本颜色
         * 
         * @param hightLight 16进制
         * @return
         */
        public RStyle hightLight(String hightLight) {
            this.hightLight = hightLight;
            Highlight hl = factory.createHighlight();
            hl.setVal(hightLight);
            getRPr().setHighlight(hl);
            return this;
        }

        /**
         * 设置删除线
         * 
         * @param strike
         * @return
         */
        public RStyle strike(boolean strike) {
            this.strike = strike;
            getRPr().setStrike(createBooleanDefaultTrue(strike));
            return this;
        }

        /**
         * 设置双删除线
         * 
         * @param dStrike
         * @return
         */
        public RStyle dStrike(boolean dStrike) {
            this.dStrike = dStrike;
            getRPr().setDstrike(createBooleanDefaultTrue(dStrike));
            return this;
        }

        /**
         * 设置字符边框宽度
         * 
         * @param borderWidth
         * @return
         */
        public RStyle borderWidth(long borderWidth) {
            this.borderWidth = borderWidth;
            getBdr().setSz(BigInteger.valueOf(borderWidth));
            return this;
        }

        /**
         * 设置字符边框类型
         * 
         * @param border
         * @return
         */
        public RStyle border(STBorder border) {
            this.border = border;
            getBdr().setVal(border);
            return this;
        }

        /**
         * 设置字符边框颜色
         * 
         * @param borderColor
         * @return
         */
        public RStyle borderColor(String borderColor) {
            this.borderColor = borderColor;
            getBdr().setColor(borderColor);
            return this;
        }

        /**
         * 设置字符边框空间
         * 
         * @param borderSpace
         * @return
         */
        public RStyle borderSpace(long borderSpace) {
            this.borderSpace = borderSpace;
            getBdr().setSpace(BigInteger.valueOf(borderSpace));
            return this;
        }

        private CTBorder getBdr() {
            RPr rpr = getRPr();
            CTBorder ctb = rpr.getBdr();
            if (ctb == null) {
                ctb = factory.createCTBorder();
                rpr.setBdr(ctb);
            }
            return ctb;
        }

        /**
         * 设置着重号
         * 
         * @param em
         * @return
         */
        public RStyle em(STEm em) {
            this.em = em;
            CTEm em0 = factory.createCTEm();
            em0.setVal(em);
            getRPr().setEm(em0);
            return this;
        }

        /**
         * 设置字符空心
         * 
         * @param outline
         * @return
         */
        public RStyle outline(boolean outline) {
            this.outline = outline;
            getRPr().setOutline(createBooleanDefaultTrue(outline));
            return this;
        }

        /**
         * 设置阴文
         * 
         * @param imprint
         * @return
         */
        public RStyle imprint(boolean imprint) {
            this.imprint = imprint;
            getRPr().setImprint(createBooleanDefaultTrue(imprint));
            return this;
        }

        /**
         * 设置阳文
         * 
         * @param emboss
         * @return
         */
        public RStyle emboss(boolean emboss) {
            this.emboss = emboss;
            getRPr().setEmboss(createBooleanDefaultTrue(emboss));
            return this;
        }

        /**
         * 设置上标下标
         * 
         * @param verticalAlignRun
         * @return
         */
        public RStyle verticalAlignRun(STVerticalAlignRun verticalAlignRun) {
            this.verticalAlignRun = verticalAlignRun;
            CTVerticalAlignRun ctvar = factory.createCTVerticalAlignRun();
            ctvar.setVal(verticalAlignRun);
            getRPr().setVertAlign(ctvar);
            return this;
        }

        /**
         * 设置字符间距缩进
         * 
         * @param scale
         * @return
         */
        public RStyle scale(int scale) {
            this.scale = scale;
            CTTextScale ctts = factory.createCTTextScale();
            ctts.setVal(scale);
            getRPr().setW(ctts);
            return this;
        }

        /**
         * 字符间距
         * 
         * @param spacing
         * @return
         */
        public RStyle spacing(long spacing) {
            this.spacing = spacing;
            CTSignedTwipsMeasure ctstm = factory.createCTSignedTwipsMeasure();
            ctstm.setVal(BigInteger.valueOf(spacing));
            getRPr().setSpacing(ctstm);
            return this;
        }

        /**
         * 设置文本位置
         * 
         * @param position
         * @return
         */
        public RStyle position(long position) {
            this.position = position;
            CTSignedHpsMeasure ctPosition = factory.createCTSignedHpsMeasure();
            ctPosition.setVal(BigInteger.valueOf(position));
            getRPr().setPosition(ctPosition);
            return this;
        }

        /**
         * 设置设置隐藏
         * 
         * @param vanish
         * @return
         */
        public RStyle vanish(boolean vanish) {
            this.vanish = vanish;
            getRPr().setVanish(createBooleanDefaultTrue(vanish));
            return this;
        }

        /**
         * 设置阴影
         * 
         * @param shadow
         * @return
         */
        public RStyle shadow(boolean shadow) {
            this.shadow = shadow;
            getRPr().setShadow(createBooleanDefaultTrue(shadow));
            return this;
        }

        /**
         * 设置底纹
         * 
         * @param shd
         * @return
         */
        public RStyle shd(STShd shd) {
            this.shd = shd;
            CTShd shd0 = factory.createCTShd();
            shd0.setVal(shd);
            getRPr().setShd(shd0);
            return this;
        }

        private RPr getRPr() {
            RPr rpr = currentR.getRPr();
            if (rpr == null) {
                rpr = factory.createRPr();
                currentR.setRPr(rpr);
            }
            return rpr;
        }

    }
    
    /**
     * 单元格
     * 
     * @author fengwk
     *
     */
    public class Table implements Serializable {

        private static final long serialVersionUID = -5015184005344731592L;

        private Tbl tbl;

        /**
         * 使用行和列构造一个表格对象
         * 
         * @param rows
         * @param cols
         */
        private Table(int rows, int cols) {
            rows = Math.max(1, rows);
            cols = Math.max(1, cols);
            int widthTwips = docWidth();
            int colWidth = widthTwips / rows;
            int[] widthArr = new int[cols];
            for (int i = 0; i < cols; i++) {
                widthArr[i] = colWidth;
            }
            tbl = createTable(rows, cols, widthArr);
        }

        /**
         * 使用已有的tbl对象构造表格
         * 
         * @param tbl
         */
        private Table(Tbl tbl) {
            this.tbl = tbl;
        }

        private Tbl createTable(int rowNum, int colsNum, int[] widthArr) {
            colsNum = Math.max(1, Math.min(colsNum, widthArr.length));
            rowNum = Math.max(1, rowNum);
            Tbl tbl = factory.createTbl();
            // 新增行
            for (int j = 0; j < rowNum; j++) {
                Tr tr = factory.createTr();
                tbl.getContent().add(tr);
                // 列
                for (int i = 0; i < colsNum; i++) {
                    Tc tc = factory.createTc();
                    TcPr tcPr = tc.getTcPr();
                    if (tcPr == null) {
                        tcPr = factory.createTcPr();
                    }
                    P p = factory.createP();
                    TblWidth cellWidth = factory.createTblWidth();
                    cellWidth.setType("dxa");
                    cellWidth.setW(BigInteger.valueOf(widthArr[i]));
                    tcPr.setTcW(cellWidth);
                    tc.setTcPr(tcPr);
                    tr.getContent().add(tc);
                    tc.getContent().add(p);
                }
            }
            return tbl;
        }

        /**
         * 将当前表格对象挂载到文档中
         * 
         * @return
         */
        public Docx append() {
            wmlp.getMainDocumentPart().addObject(tbl);
            return Docx.this;
        }
        
        /**
         * 设置表格总宽度
         * 
         * @param width
         */
        public Table width(long width) {
            TblPr tblPr = getTblPr();
            TblWidth tblW = tblPr.getTblW();
            if (tblW == null) {
                tblW = new TblWidth();
                tblPr.setTblW(tblW);
            }
            tblW.setW(BigInteger.valueOf(width));
            tblW.setType("dxa");
            return this;
        }

        /**
         * 设置单元格宽度
         * 
         * @param row
         * @param col
         * @param width
         */
        public Table cellWidth(int row, int col, long width) {
            cellWidth(getTc(row, col), width);
            return this;
        }

        private void cellWidth(Tc tc, long width) {
            TcPr tcPr = getTcPr(tc);
            TblWidth tcW = tcPr.getTcW();
            if (tcW == null) {
                tcW = new TblWidth();
                tcPr.setTcW(tcW);
            }
            tcW.setW(BigInteger.valueOf(width));
            tcW.setType("dxa");
        }

        /**
         * 设置指定行高度
         * 
         * @param row
         * @param heigth
         */
        public void rowHeight(int row, String heigth) {
            TrPr trPr = getTrPr(getTr(row));
            CTHeight ctHeight = new CTHeight();
            ctHeight.setVal(new BigInteger(heigth));
            TrHeight trHeight = new TrHeight(ctHeight);
            trHeight.set(trPr);
        }

        /**
         * 设置单元格上边框
         * 
         * @param borderTop
         * @return
         */
        public Table borderTop(CTBorder borderTop) {
            TblBorders().setTop(borderTop);
            return this;
        }

        /**
         * 设置单元格右边框
         * 
         * @param borderRight
         * @return
         */
        public Table borderRight(CTBorder borderRight) {
            TblBorders().setRight(borderRight);
            return this;
        }

        /**
         * 设置单元格下边框
         * 
         * @param borderBottom
         * @return
         */
        public Table borderBottom(CTBorder borderBottom) {
            TblBorders().setBottom(borderBottom);
            return this;
        }

        /**
         * 设置单元格左边框
         * 
         * @param borderLeft
         * @return
         */
        public Table borderLeft(CTBorder borderLeft) {
            TblBorders().setLeft(borderLeft);
            return this;
        }

        private TblBorders TblBorders() {
            TblPr tblPr = getTblPr();
            TblBorders borders = tblPr.getTblBorders();
            if (borders == null) {
                borders = new TblBorders();
                tblPr.setTblBorders(borders);
            }
            return borders;
        }

        /**
         * 设置表格水平对齐方式(仅对表格起作用,单元格不一定水平对齐)
         * 
         * @param align
         * @return
         */
        public Table align(JcEnumeration align) {
            TblPr tblPr = getTblPr();
            Jc jc = tblPr.getJc();
            if (jc == null) {
                jc = factory.createJc();
                tblPr.setJc(jc);
            }
            jc.setVal(align);
            return this;
        }

        /**
         * 设置所有单元格水平对齐方式,只对该方法前面产生的单元格起作用
         * 
         * @param jcType
         */
        public Table allCellAlign(JcEnumeration align) {
            List<Tr> trList = getTblAllTr();
            for (Tr tr : trList) {
                List<Tc> tcList = getTrAllCell(tr);
                for (Tc tc : tcList) {
                    cellAlign(tc, align);
                }
            }
            return this;
        }

        /**
         * 设置单元格水平对齐方式
         * 
         * @param row
         * @param col
         * @param align
         * @return
         */
        public Table cellAlign(int row, int col, JcEnumeration align) {
            cellAlign(getTc(row, col), align);
            return this;
        }

        // 设置单元格水平对齐方式
        private Table cellAlign(Tc tc, JcEnumeration align) {
            List<P> pList = getTcAllP(tc);
            if (CollectionUtils.isNotBlank(pList)) {
                for (P p : pList)
                    pAlign(p, align);
            }
            return this;
        }

        // 设置段落水平对齐方式
        public void pAlign(P p, JcEnumeration jcAlign) {
            if (jcAlign != null) {
                PPr ppr = p.getPPr();
                if (ppr == null) {
                    ppr = factory.createPPr();
                    p.setPPr(ppr);
                }
                Jc align = factory.createJc();
                align.setVal(jcAlign);
                ppr.setJc(align);
            }
        }

        /**
         * 设置表格垂直对齐方式(包括单元格),只对该方法前面产生的单元格起作用
         * 
         * @param vAlignType
         */
        public Table valign(STVerticalJc valign) {
            List<Tr> trList = getTblAllTr();
            for (Tr tr : trList) {
                List<Tc> tcList = getTrAllCell(tr);
                for (Tc tc : tcList) {
                    cellValign(tc, valign);
                }
            }
            return this;
        }

        /**
         * 设置单元格垂直对齐方式
         * 
         * @param tc
         * @param valign
         */
        public Table cellValign(int row, int col, STVerticalJc valign) {
            cellValign(getTc(row, col), valign);
            return this;
        }

        private void cellValign(Tc tc, STVerticalJc valign) {
            TcPr tcPr = getTcPr(tc);
            CTVerticalJc vAlign = new CTVerticalJc();
            vAlign.setVal(valign);
            tcPr.setVAlign(vAlign);
        }

        /**
         * 单元格上边距
         * 
         * @param marginTop
         * @return
         */
        public Table marginTop(long marginTop) {
            TblWidth w = factory.createTblWidth();
            w.setW(BigInteger.valueOf(marginTop));
            w.setType("dxa");
            getCTTblCellMar().setTop(w);
            return this;
        }

        /**
         * 单元格右边距
         * 
         * @param marginRight
         * @return
         */
        public Table marginRight(long marginRight) {
            TblWidth w = factory.createTblWidth();
            w.setW(BigInteger.valueOf(marginRight));
            w.setType("dxa");
            getCTTblCellMar().setRight(w);
            return this;
        }

        /**
         * 单元格下边距
         * 
         * @param marginBottom
         * @return
         */
        public Table marginBottom(long marginBottom) {
            TblWidth w = factory.createTblWidth();
            w.setW(BigInteger.valueOf(marginBottom));
            w.setType("dxa");
            getCTTblCellMar().setBottom(w);
            return this;
        }

        /**
         * 单元格左边距
         * 
         * @param marginLeft
         * @return
         */
        public Table marginLeft(long marginLeft) {
            TblWidth w = factory.createTblWidth();
            w.setW(BigInteger.valueOf(marginLeft));
            w.setType("dxa");
            getCTTblCellMar().setLeft(w);
            return this;
        }

        private CTTblCellMar getCTTblCellMar() {
            TblPr tblPr = getTblPr();
            CTTblCellMar cellMar = tblPr.getTblCellMar();
            if (cellMar == null) {
                cellMar = new CTTblCellMar();
                tblPr.setTblCellMar(cellMar);
            }
            return cellMar;
        }

        /**
         * 跨列合并
         * 
         * @param row
         * @param fromCell
         * @param toCell
         */
        public Table mergeCol(int row, int fromCell, int toCell) {
            if (row < 0 || fromCell < 0 || toCell < 0)
                return this;
            List<Tr> trList = getTblAllTr();
            if (row > trList.size())
                return this;
            Tr tr = trList.get(row);
            List<Tc> tcList = getTrAllCell(tr);
            for (int cellIndex = fromCell, len = Math.min(tcList.size() - 1, toCell); cellIndex <= len; cellIndex++) {
                Tc tc = tcList.get(cellIndex);
                TcPr tcPr = getTcPr(tc);
                HMerge hMerge = tcPr.getHMerge();
                if (hMerge == null) {
                    hMerge = new HMerge();
                    tcPr.setHMerge(hMerge);
                }
                if (cellIndex == fromCell)
                    hMerge.setVal("restart");
                else
                    hMerge.setVal("continue");
            }
            return this;
        }

        /**
         * 跨行合并
         * 
         * @param col
         * @param fromRow
         * @param toRow
         */
        public Table mergeRow(int col, int fromRow, int toRow) {
            if (col < 0 || fromRow < 0 || toRow < 0) {
                return this;
            }
            for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
                Tc tc = getTc(rowIndex, col);
                if (tc == null) {
                    break;
                }
                TcPr tcPr = getTcPr(tc);
                VMerge vMerge = tcPr.getVMerge();
                if (vMerge == null) {
                    vMerge = new VMerge();
                    tcPr.setVMerge(vMerge);
                }
                if (rowIndex == fromRow) {
                    vMerge.setVal("restart");
                } else {
                    vMerge.setVal("continue");
                }
            }
            return this;
        }

        /**
         * 在表格指定位置新增一行(默认按表格定义的列数添加)
         * 
         * @param index
         * @param valign
         * @param align
         */
        public Table addRow(int index, STVerticalJc valign, JcEnumeration align) {
            TblGrid tblGrid = tbl.getTblGrid();
            Tr tr = new Tr();
            if (tblGrid != null) {
                List<TblGridCol> gridList = tblGrid.getGridCol();
                for (TblGridCol tblGridCol : gridList) {
                    Tc tc = new Tc();
                    cellWidth(tc, tblGridCol.getW().longValue());
                    if (valign != null)
                        cellValign(tc, valign);
                    P p = new P();
                    if (align != null) {
                        PPr pPr = new PPr();
                        Jc jc = new Jc();
                        jc.setVal(align);
                        pPr.setJc(jc);
                        p.setPPr(pPr);
                    }
                    R run = new R();
                    p.getContent().add(run);
                    tc.getContent().add(p);
                    tr.getContent().add(tc);
                }
            } else {
                // 大部分情况都不会走到这一步
                Tr firstTr = getTblAllTr().get(0);
                int cellSize = getTcCellSizeWithMergeNum(firstTr);
                for (int i = 0; i < cellSize; i++) {
                    Tc tc = new Tc();
                    if (valign != null)
                        cellValign(tc, valign);
                    P p = new P();
                    if (align != null) {
                        PPr pPr = new PPr();
                        Jc jc = new Jc();
                        // 单元格居中对齐
                        jc.setVal(align);
                        pPr.setJc(jc);
                        p.setPPr(pPr);
                    }
                    R run = new R();
                    p.getContent().add(run);
                    tc.getContent().add(p);
                    tr.getContent().add(tc);
                }
            }
            if (index >= 0 && index < tbl.getContent().size()) {
                tbl.getContent().add(index, tr);
            } else {
                tbl.getContent().add(tr);
            }
            return this;
        }

        /**
         * 设置单元格值
         * 
         * @param row
         * @param cell
         * @param value
         */
        public Table setCell(int row, int cell, String value) {
            Tc tc = getTc(row, cell);
            List<P> pList = getTcAllP(tc);
            if (CollectionUtils.isBlank(pList)) {
                P p = factory.createP();
                R r = factory.createR();
                p.getContent().add(r);
                r.getContent().add(value);
                setTcP(row, cell, p);
            } else {
                P p = pList.get(pList.size() - 1);
                List<Object> oList = p.getContent();
                R r = null;
                if (CollectionUtils.isNotBlank(oList)) {
                    for (Object o : oList) {
                        if (o instanceof R) {
                            r = (R) o;
                        }
                    }
                }
                if (r == null) {
                    r = factory.createR();
                    p.getContent().add(r);
                }

                Text text = createText(value);
                r.getContent().add(text);
            }
            return this;
        }

        /**
         * 若表格已经在文档中,则移除当前表格
         * 
         * @return
         */
        public boolean remove() {
            boolean flag = false;
            List<Object> objList = wmlp.getMainDocumentPart().getContent();
            if (CollectionUtils.isNotBlank(objList)) {
                for (int i = 0, len = objList.size(); i < len; i++) {
                    Object obj = XmlUtils.unwrap(objList.get(i));
                    if (obj instanceof Tbl) {
                        if (obj == tbl) {
                            wmlp.getMainDocumentPart().getContent().remove(i);
                            flag = true;
                            break;
                        }
                    }
                }
            }
            return flag;
        }

        /**
         * 删除指定行 删除后行数减一
         * 
         * @param index
         * @return
         */
        public boolean removeRow(int index) {
            boolean flag = false;
            if (index < 0)
                return flag;
            List<Object> objList = tbl.getContent();
            if (CollectionUtils.isNotBlank(objList)) {
                int k = -1;
                for (int i = 0, len = objList.size(); i < len; i++) {
                    Object obj = XmlUtils.unwrap(objList.get(i));
                    if (obj instanceof Tr) {
                        k++;
                        if (k == index) {
                            tbl.getContent().remove(i);
                            flag = true;
                            break;
                        }
                    }
                }
            }
            return flag;
        }

        /**
         * 获取单元格内容
         * 
         * @param row
         * @param col
         * @return
         */
        public String getContent(int row, int col) {
            return getElementContent(getTc(row, col));
        }

        /**
         * 获取单元格内容的字符串形式,无分割符
         * 
         * @return
         */
        public String getContentStr() {
            String content = null;
            try {
                content = getElementContent(tbl);
            } catch (Exception e) {
                LOG.error(e);
            }
            return content;
        }

        /**
         * 获取表格内容
         * 
         * @return
         */
        public List<String> getContentList() {
            List<String> resultList = new ArrayList<String>();
            List<Tr> trList = getTblAllTr();
            for (Tr tr : trList) {
                StringBuffer sb = new StringBuffer();
                List<Tc> tcList = getTrAllCell(tr);
                for (Tc tc : tcList) {
                    try {
                        sb.append(getElementContent(tc) + ",");
                    } catch (Exception e) {
                        LOG.error(e);
                    }
                }
                resultList.add(sb.toString());
            }
            return resultList;
        }

        /**
         * 隐藏行(只对表格中间的部分起作用,不包括首尾行)
         * 
         * @param row
         * @param hidden
         */
        public void vanishRow(int row, boolean hidden) {
            List<Tc> tcList = getTrAllCell(getTr(row));
            for (Tc tc : tcList) {
                vanish(tc, hidden);
            }
        }

        /**
         * 设置隐藏单元格内容
         * 
         * @param row
         * @param col
         * @param vanish
         */
        public void vanish(int row, int col, boolean vanish) {
            vanish(getTc(row, col), vanish);
        }

        private void vanish(Tc tc, boolean vanish) {
            List<P> pList = getTcAllP(tc);
            for (P p : pList) {
                List<Object> objRList = getAllElementFromObject(p, R.class);
                if (objRList == null) {
                    continue;
                }
                for (Object objR : objRList) {
                    if (objR instanceof R) {
                        R r = (R) objR;
                        RPr rpr = r.getRPr();
                        if (rpr == null) {
                            rpr = factory.createRPr();
                            r.setRPr(rpr);
                        }
                        rpr.setVanish(createBooleanDefaultTrue(vanish));
                    }
                }
                PPr ppr = p.getPPr();
                if (ppr == null) {
                    ppr = factory.createPPr();
                    p.setPPr(ppr);
                }
                ParaRPr parRpr = ppr.getRPr();
                if (parRpr == null) {
                    parRpr = new ParaRPr();
                    ppr.setRPr(parRpr);
                }
                parRpr.setVanish(createBooleanDefaultTrue(vanish));
            }
        }

        // 得到表格所有的行
        private List<Tr> getTblAllTr() {
            List<Object> objList = getAllElementFromObject(tbl, Tr.class);
            List<Tr> trList = new ArrayList<Tr>();
            if (objList == null)
                return trList;
            for (Object obj : objList) {
                if (obj instanceof Tr) {
                    Tr tr = (Tr) obj;
                    trList.add(tr);
                }
            }
            return trList;
        }

        // 设置单元格的P
        private void setTcP(int row, int cell, P p) {
            Tc tc = getTc(row, cell);
            if (tc != null)
                tc.getContent().add(p);
        }

        private Tr getTr(int row) {
            if (row < 0)
                return null;
            List<Tr> trList = getTblAllTr();
            if (row >= trList.size())
                return null;
            return trList.get(row);
        }

        // 得到指定位置的单元格
        private Tc getTc(int row, int col) {
            if (row < 0 || col < 0)
                return null;
            List<Tc> tcList = getTrAllCell(getTr(row));
            if (col >= tcList.size())
                return null;
            return tcList.get(col);
        }

        private TblPr getTblPr() {
            TblPr tblPr = tbl.getTblPr();
            if (tblPr == null) {
                tblPr = new TblPr();
                tbl.setTblPr(tblPr);
            }
            return tblPr;
        }

        // 得到行的列数
        private int getTcCellSizeWithMergeNum(Tr tr) {
            int cellSize = 1;
            List<Tc> tcList = getTrAllCell(tr);
            if (tcList == null || tcList.size() == 0) {
                return cellSize;
            }
            cellSize = tcList.size();
            for (Tc tc : tcList) {
                TcPr tcPr = getTcPr(tc);
                GridSpan gridSpan = tcPr.getGridSpan();
                if (gridSpan != null) {
                    cellSize += gridSpan.getVal().intValue() - 1;
                }
            }
            return cellSize;
        }

        private TrPr getTrPr(Tr tr) {
            TrPr trPr = tr.getTrPr();
            if (trPr == null) {
                trPr = new TrPr();
                tr.setTrPr(trPr);
            }
            return trPr;
        }

        private List<P> getTcAllP(Tc tc) {
            List<Object> objList = getAllElementFromObject(tc, P.class);
            List<P> pList = new ArrayList<P>();
            if (objList == null) {
                return pList;
            }
            for (Object obj : objList) {
                if (obj instanceof P) {
                    P p = (P) obj;
                    pList.add(p);
                }
            }
            return pList;
        }

        private TcPr getTcPr(Tc tc) {
            TcPr tcPr = tc.getTcPr();
            if (tcPr == null) {
                tcPr = new TcPr();
                tc.setTcPr(tcPr);
            }
            return tcPr;
        }

        // 获取所有的单元格
        private List<Tc> getTrAllCell(Tr tr) {
            List<Object> objList = getAllElementFromObject(tr, Tc.class);
            List<Tc> tcList = new ArrayList<Tc>();
            if (objList == null) {
                return tcList;
            }
            for (Object tcObj : objList) {
                if (tcObj instanceof Tc) {
                    Tc objTc = (Tc) tcObj;
                    tcList.add(objTc);
                }
            }
            return tcList;
        }

    }

}
